package com.ldg.search.service.impl;

import com.alibaba.fastjson.JSON;
import com.ldg.search.entity.ItemEntity;
import com.ldg.search.entity.ItemVo;
import com.ldg.search.service.ItemService;
import org.apache.commons.lang3.StringUtils;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.aggregations.Aggregation;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.Aggregations;
import org.elasticsearch.search.aggregations.bucket.MultiBucketsAggregation;
import org.elasticsearch.search.aggregations.bucket.terms.ParsedStringTerms;
import org.elasticsearch.search.aggregations.bucket.terms.Terms;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.sort.SortBuilders;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.elasticsearch.core.ElasticsearchRestTemplate;
import org.springframework.data.elasticsearch.core.SearchHit;
import org.springframework.data.elasticsearch.core.SearchHits;
import org.springframework.data.elasticsearch.core.mapping.IndexCoordinates;
import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * @Author: Liudao
 * @Date: 2022-09-22-14:52
 * @Description:
 */
@Service
public class ItemServiceImpl implements ItemService {
    @Autowired
    private ElasticsearchRestTemplate restTemplate;
    @Autowired
    private StringRedisTemplate redisTemplate;

    @Override
    public Map<String, Object> search(ItemVo vo) {
        //定义要返回的map对象
        Map<String, Object> map = new HashMap<>();
        //处理查询中的关键字
        if (StringUtils.isBlank(vo.getKeywords())){
            vo.setKeywords("");
        }
        //定义NativeSearchQueryBuilder
        NativeSearchQueryBuilder nativeSearchQueryBuilder = new NativeSearchQueryBuilder();
        //定义多关键字查询
        nativeSearchQueryBuilder.withQuery(QueryBuilders.multiMatchQuery(vo.getKeywords(),"title","brand","category"));
        //定义高亮查询
        nativeSearchQueryBuilder.withHighlightBuilder(new HighlightBuilder().field("title").preTags("<font color='red'>").postTags("</font>"));
        //进行分类的分组查询
        nativeSearchQueryBuilder.addAggregation(AggregationBuilders.terms("categoryGroup").field("category.keyword"));
        //定义组合查询对象
        //定义BoolQueryBuilder
        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
        //开始添加过滤查询条件
        //开始分类查询
        if(StringUtils.isNotBlank(vo.getCategory())){
            boolQueryBuilder.filter(QueryBuilders.termQuery("category.keyword",vo.getCategory()));
        }
        //开始品牌查询
        if(StringUtils.isNotBlank(vo.getBrand())){
            boolQueryBuilder.filter(QueryBuilders.termQuery("brand.keyword",vo.getBrand()));
        }
        //开始规格及规格选项的查询
        if(vo.getSpec() != null && vo.getSpec().size() > 0){
            List<String> spec = vo.getSpec();
            for (String s : spec) {
                String[] split = s.split(":");          // 拆分后：split[0]: 规格名字 split[1]: 规格选项的值
                boolQueryBuilder.filter(QueryBuilders.termQuery("specMap." + split[0] + ".keyword",split[1]));
            }
        }
        //开始价格区间的查询
        if(StringUtils.isNotBlank(vo.getPrice())){
            //拆分价格区间为最小价格与最大价格
            String[] split = vo.getPrice().split("-");
            //特殊情况处理
            if(split[1].equals("*")){
                boolQueryBuilder.filter(QueryBuilders.rangeQuery("price").gte(split[0]));
            }else{
                boolQueryBuilder.filter(QueryBuilders.rangeQuery("price").gt(split[0]).lte(split[1]));
            }
        }
        //进行价格排序
        if(StringUtils.isNotBlank(vo.getSort())){
            //拆分排序字段
            String[] split = vo.getSort().split(":");       // split[0]: 排序字段 split[1]: 排序状态（asc/desc）
            //开始排序
            if(split[1].equals("asc")){
                nativeSearchQueryBuilder.withSort(SortBuilders.fieldSort(split[0]).order(SortOrder.ASC));
            }else{
                nativeSearchQueryBuilder.withSort(SortBuilders.fieldSort(split[0]).order(SortOrder.DESC));
            }
        }
        //进行分页查询
        int page = vo.getPage();
        int pagesize = vo.getPageSize();


        //将上面的BoolQueryBuilder与NativeSearchQueryBuilder进行绑定
        nativeSearchQueryBuilder.withFilter(boolQueryBuilder);

        nativeSearchQueryBuilder.withPageable(PageRequest.of(page-1,pagesize));

        //开始搜索查询
        SearchHits<ItemEntity> searchHits = restTemplate.search(nativeSearchQueryBuilder.build(), ItemEntity.class, IndexCoordinates.of("item"));
        long totalHits = searchHits.getTotalHits();
        //计算总页数
        long totalPage = (long) Math.ceil(totalHits/(double)pagesize);
        //定义查询到商品的集合
        List<ItemEntity> entities = new ArrayList<>();
        List<SearchHit<ItemEntity>> searchHits1 = searchHits.getSearchHits();
        for (SearchHit<ItemEntity> itemEntitySearchHit : searchHits1) {
            //得到高亮查询数据
            List<String> title = itemEntitySearchHit.getHighlightField("title");
            //得到高亮查询原始对象
            ItemEntity content = itemEntitySearchHit.getContent();
            //修改高亮原始对象的标题部分
            if (!CollectionUtils.isEmpty(title)){
                content.setTitle(title.get(0));
            }
            //将修改后的高亮对象放入
            entities.add(content);
        }
        //取得分组的数据
        Aggregations aggregations = searchHits.getAggregations();
        //取得具体分组
        assert aggregations != null;
        ParsedStringTerms categoryGroup = aggregations.get("categoryGroup");
        //得到分组下的分桶数据
        List<? extends Terms.Bucket> buckets = categoryGroup.getBuckets();
        //遍历所有的分桶，key就是分类的名字
        List<String> categoryList = buckets.stream().map(MultiBucketsAggregation.Bucket::getKeyAsString).collect(Collectors.toList());

        //处理分类
        //前台传入的category为null
        String cate = "";
        Map brandAndSpecList = new HashMap();
        cate = vo.getCategory();
        if(!CollectionUtils.isEmpty(categoryList)){
            if(StringUtils.isBlank(cate)){
                cate = categoryList.get(0);
            }
            //根据分类名字查询品牌及规格列表
            brandAndSpecList = findBrandAndSpec(cate);
        }else{
            brandAndSpecList.put("specList",new ArrayList<>());
            brandAndSpecList.put("brandList",new ArrayList<>());
        }
        map.put("rows",entities);
        map.put("total",totalHits);
        map.put("categoryList",categoryList);
        map.put("totalPage",totalPage);
        map.putAll(brandAndSpecList);
        return map;
    }

    private Map findBrandAndSpec(String cate) {
        //根据分类名找到模板id
        String typeId = (String) redisTemplate.boundHashOps("itemCats").get(cate);
        //根据模板id找到品牌及规格列表
        String brandListStr = (String) redisTemplate.boundHashOps("brandList").get(typeId);
        //将上面的品牌列表字符串转换为集合
        List<Map> brandList = JSON.parseArray(brandListStr,Map.class);
        //根据模板id找到规格及规格选项列表
        String specListStr = (String) redisTemplate.boundHashOps("specList").get(typeId);
        //将规格及规格选项列表转换为集合
        List<Map> specList = JSON.parseArray(specListStr, Map.class);
        //定义返回结果Map
        Map map = new HashMap();
        // 将品牌及规格列表放到map中
        map.put("brandList",brandList);
        map.put("specList",specList);
        //返回
        return map;
    }
}
